/*
 * PCB elegance (Open source tools for making printed circuit boards)
 *
 * Copyright (C) 2012  Herman Morsink Vollenbroek
 *
 * File: newapis.h
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*******************************************************************************************/

/*
 *  Copyright (c) 1997-1999, Microsoft Corporation
 *
 *  Wrapper module that "stubs" APIs that were not implemented
 *  on Windows 95 or Windows NT versions less than 4.0 SP 3.
 *
 *  By using this header, your code will run on older platforms.
 *
 *  To enable a particular wrapper, define the corresponding symbol.
 *
 *  Function                Symbol
 *
 *  GetDiskFreeSpaceEx      WANT_GETDISKFREESPACEEX_WRAPPER
 *  GetLongPathName         WANT_GETLONGPATHNAME_WRAPPER
 *  GetFileAttributesEx     WANT_GETFILEATTRIBUTESEX_WRAPPER
 *  IsDebuggerPresent       WANT_ISDEBUGGERPRESENT_WRAPPER
 *
 *  Exactly one source file must include the line
 *
 *  #define COMPILE_NEWAPIS_STUBS
 *
 *  before including this file.
 *
 */

#ifdef __cplusplus
extern "C"
{	/* Assume C declarations for C++ */
#endif							/* __cplusplus */

/*****************************************************************************
 *
 * GetDiskFreeSpaceEx
 *
 *****************************************************************************/

#ifdef WANT_GETDISKFREESPACEEX_WRAPPER

#undef GetDiskFreeSpaceEx
#define GetDiskFreeSpaceEx _GetDiskFreeSpaceEx

extern int32(CALLBACK * GetDiskFreeSpaceEx) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);

/*
 * Exactly one file should define this symbol.
 */
#ifdef COMPILE_NEWAPIS_STUBS

/*
 * The version to use if we are forced to emulate.
 */
static int32 WINAPI Emulate_GetDiskFreeSpaceEx(LPCTSTR ptszRoot, PULARGE_INTEGER pliQuota, PULARGE_INTEGER pliTotal,
        PULARGE_INTEGER pliFree)
{
	DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
	int32 fRc;

	fRc = GetDiskFreeSpace(ptszRoot, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus);

	if (fRc)
	{
		DWORD dwBytesPerClus = dwSecPerClus * dwBytesPerSec;

		/*
		 *  Curiously, of all the output parameters, only pliFree is
		 *  allowed to be NULL.
		 */

		*(__int64 *) pliQuota = Int32x32To64(dwBytesPerClus, dwFreeClus);

		if (pliFree)
			*pliFree = *pliQuota;

		*(__int64 *) pliTotal = Int32x32To64(dwBytesPerClus, dwTotalClus);
	}

	return fRc;
}

/*
 * The stub that probes to decide which version to use.
 */
static int32 WINAPI Probe_GetDiskFreeSpaceEx(LPCTSTR ptszRoot, PULARGE_INTEGER pliQuota, PULARGE_INTEGER pliTotal,
        PULARGE_INTEGER pliFree)
{
	HINSTANCE hinst;
	FARPROC fp;
	int32 fRc;
	int32(CALLBACK * RealGetDiskFreeSpaceEx) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);

	hinst = GetModuleHandle(TEXT("KERNEL32"));
#ifdef UNICODE
	fp = GetProcAddress(hinst, "GetDiskFreeSpaceExW");
#else
	fp = GetProcAddress(hinst, "GetDiskFreeSpaceExA");
#endif

	if (fp)
	{
		*(FARPROC *) & RealGetDiskFreeSpaceEx = fp;
		fRc = RealGetDiskFreeSpaceEx(ptszRoot, pliQuota, pliTotal, pliFree);

		if (fRc || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
			GetDiskFreeSpaceEx = RealGetDiskFreeSpaceEx;
		else
		{
			GetDiskFreeSpaceEx = Emulate_GetDiskFreeSpaceEx;
			fRc = GetDiskFreeSpaceEx(ptszRoot, pliQuota, pliTotal, pliFree);
		}
	}
	else
	{
		GetDiskFreeSpaceEx = Emulate_GetDiskFreeSpaceEx;
		fRc = GetDiskFreeSpaceEx(ptszRoot, pliQuota, pliTotal, pliFree);
	}

	return fRc;

}

int32(CALLBACK * GetDiskFreeSpaceEx) (LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER) =
    Probe_GetDiskFreeSpaceEx;

#endif /* COMPILE_NEWAPIS_STUBS */
#endif /* WANT_GETDISKFREESPACEEX_WRAPPER */

/*****************************************************************************
 *
 * GetLongPathName
 *
 *****************************************************************************/

#ifdef WANT_GETLONGPATHNAME_WRAPPER

#pragma warning( disable : 4201 )	// Disable warning messages 4201 (nonstandard extension used : nameless struct/union)

#include <shlobj.h>

#undef GetLongPathName
#define GetLongPathName _GetLongPathName

extern DWORD(CALLBACK * GetLongPathName) (LPCTSTR, LPTSTR, DWORD);

/*
 * Exactly one file should define this symbol.
 */
#ifdef COMPILE_NEWAPIS_STUBS

/*
 * The version to use if we are forced to emulate.
 */
static DWORD WINAPI Emulate_GetLongPathName(LPCTSTR ptszShort, LPTSTR ptszLong, DWORD ctchBuf)
{
	LPSHELLFOLDER psfDesk;
	HRESULT hr;
	LPITEMIDLIST pidl;
	TCHAR tsz[MAX_PATH];	/* Scratch TCHAR buffer */
	DWORD dwRc;
	LPMALLOC pMalloc;

	/*
	 *  The file had better exist.  GetFileAttributes() will
	 *  not only tell us, but it'll even call SetLastError()
	 *  for us.
	 */
	if (GetFileAttributes(ptszShort) == 0xFFFFFFFF)
		return 0;

	/*
	 *  First convert from relative path to absolute path.
	 *  This uses the scratch TCHAR buffer.
	 */
	dwRc = GetFullPathName(ptszShort, MAX_PATH, tsz, NULL);

	if (dwRc == 0)
	{
		/*
		 *  Failed; GFPN already did SetLastError().
		 */
	}
	else if (dwRc >= MAX_PATH)
	{
		/*
		 *  Resulting path would be too long.
		 */
		SetLastError(ERROR_BUFFER_OVERFLOW);
		dwRc = 0;
	}
	else
	{
		/*
		 *  Just right.
		 */
		hr = SHGetDesktopFolder(&psfDesk);

		if (SUCCEEDED(hr))
		{
			ULONG cwchEaten;

#ifdef UNICODE
#ifdef __cplusplus
			hr = psfDesk->ParseDisplayName(NULL, NULL, tsz, &cwchEaten, &pidl, NULL);
#else
			hr = psfDesk->lpVtbl->ParseDisplayName(psfDesk, NULL, NULL, tsz, &cwchEaten, &pidl, NULL);
#endif
#else
			WCHAR wsz[MAX_PATH];	/* Scratch WCHAR buffer */

			/*
			 *  ParseDisplayName requires UNICODE, so we use
			 *  the scratch WCHAR buffer during the conversion.
			 */
			dwRc = MultiByteToWideChar(AreFileApisANSI()? CP_ACP : CP_OEMCP, 0, tsz, -1, wsz, MAX_PATH);

			if (dwRc == 0)
			{
				/*
				 *  Couldn't convert to UNICODE.  MB2WC uses
				 *  ERROR_INSUFFICIENT_BUFFER, which we convert
				 *  to ERROR_BUFFER_OVERFLOW.  Any other error
				 *  we leave alone.
				 */
				if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
					SetLastError(ERROR_BUFFER_OVERFLOW);

				dwRc = 0;
			}
			else
			{
#ifdef __cplusplus
				hr = psfDesk->ParseDisplayName(NULL, NULL, wsz, &cwchEaten, &pidl, NULL);
#else
				hr = psfDesk->lpVtbl->ParseDisplayName(psfDesk, NULL, NULL, wsz, &cwchEaten, &pidl, NULL);
#endif
#endif

			if (FAILED(hr))
			{
				/*
				 *  Weird.  Convert the result back to a Win32
				 *  error code if we can.  Otherwise, use the
				 *  generic "duh" error code ERROR_INVALID_DATA.
				 */
				if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
					SetLastError(HRESULT_CODE(hr));
				else
					SetLastError(ERROR_INVALID_DATA);

				dwRc = 0;
			}
			else
			{
				/*
				 *  Convert the pidl back to a filename in the
				 *  TCHAR scratch buffer.
				 */
				dwRc = SHGetPathFromIDList(pidl, tsz);

				if (dwRc == 0 && tsz[0])
				{
					/*
					 *  Bizarre failure.
					 */
					SetLastError(ERROR_INVALID_DATA);
				}
				else
				{
					/*
					 *  Copy the result back to the user's buffer.
					 */
					dwRc = lstrlen(tsz);

					if (dwRc + 1 > ctchBuf)
					{
						/*
						 *  On buffer overflow, return necessary
						 *  size including terminating null (+1).
						 */
						SetLastError(ERROR_INSUFFICIENT_BUFFER);
						dwRc = dwRc + 1;
					}
					else
					{
						/*
						 *  On buffer okay, return actual size not
						 *  including terminating null.
						 */
						lstrcpyn(ptszLong, tsz, ctchBuf);
					}
				}

				/*
				 *  Free the pidl.
				 */
				if (SUCCEEDED(SHGetMalloc(&pMalloc)))
				{
#ifdef __cplusplus
					pMalloc->Free(pidl);
					pMalloc->Release();
#else
					pMalloc->lpVtbl->Free(pMalloc, pidl);
					pMalloc->lpVtbl->Release(pMalloc);
#endif
				}
			}

#ifndef UNICODE
		}

#endif
		/*
		 *  Release the desktop folder now that we no longer
		 *  need it.
		 */
#ifdef __cplusplus
		psfDesk->Release();
#else
		psfDesk->lpVtbl->Release(psfDesk);
#endif
	}
}

return dwRc;
}

/*
 * The stub that probes to decide which version to use.
 */
static DWORD WINAPI Probe_GetLongPathName(LPCTSTR ptszShort, LPTSTR ptszLong, DWORD ctchBuf)
{
	HINSTANCE hinst;
	FARPROC fp;
	DWORD dwRc;
	int32(CALLBACK * RealGetLongPathName) (LPCTSTR, LPTSTR, DWORD);

	hinst = GetModuleHandle(TEXT("KERNEL32"));
#ifdef UNICODE
	fp = GetProcAddress(hinst, "GetLongPathNameW");
#else
	fp = GetProcAddress(hinst, "GetLongPathNameA");
#endif

	if (fp)
	{
		*(FARPROC *) & RealGetLongPathName = fp;
		dwRc = RealGetLongPathName(ptszShort, ptszLong, ctchBuf);

		if (dwRc || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
			GetLongPathName = RealGetLongPathName;
		else
		{
			GetLongPathName = Emulate_GetLongPathName;
			dwRc = GetLongPathName(ptszShort, ptszLong, ctchBuf);
		}
	}
	else
	{
		GetLongPathName = Emulate_GetLongPathName;
		dwRc = GetLongPathName(ptszShort, ptszLong, ctchBuf);
	}

	return dwRc;

}

DWORD(CALLBACK * GetLongPathName) (LPCTSTR, LPTSTR, DWORD) = Probe_GetLongPathName;

#endif /* COMPILE_NEWAPIS_STUBS */
#endif /* WANT_GETLONGPATHNAME_WRAPPER */

/*****************************************************************************
 *
 * GetFileAttributesEx
 *
 *****************************************************************************/

#ifdef WANT_GETFILEATTRIBUTESEX_WRAPPER

#undef GetFileAttributesEx
#define GetFileAttributesEx _GetFileAttributesEx

/*
 *  Really old header files don't even have definitions for these constants
 *  and structures.
 */
#if WINVER < 0x040A

typedef enum _GET_FILEEX_INFO_LEVELS
{
	GetFileExInfoStandard,
	GetFileExMaxInfoLevel
} GET_FILEEX_INFO_LEVELS;

typedef struct _WIN32_FILE_ATTRIBUTE_DATA
{
	DWORD dwFileAttributes;
	FILETIME ftCreationTime;
	FILETIME ftLastAccessTime;
	FILETIME ftLastWriteTime;
	DWORD nFileSizeHigh;
	DWORD nFileSizeLow;
} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;

#endif

extern int32(CALLBACK * GetFileAttributesEx) (LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID);

/*
 * Exactly one file should define this symbol.
 */
#ifdef COMPILE_NEWAPIS_STUBS

/*
 * The version to use if we are forced to emulate.
 */
static int32 WINAPI Emulate_GetFileAttributesEx(LPCTSTR ptszFile, GET_FILEEX_INFO_LEVELS level, LPVOID pv)
{
	int32 fRc;

	if (level == GetFileExInfoStandard)
	{

		/*
		 *  Must call GetFileAttributes first to avoid returning random
		 *  values if the so-called filename contains wildcards.
		 */
		if (GetFileAttributes(ptszFile) != 0xFFFFFFFF)
		{
			HANDLE hfind;
			WIN32_FIND_DATA wfd;
			hfind = FindFirstFile(ptszFile, &wfd);

			if (hfind != INVALID_HANDLE_VALUE)
			{
				LPWIN32_FILE_ATTRIBUTE_DATA pfad = pv;
				FindClose(hfind);

				pfad->dwFileAttributes = wfd.dwFileAttributes;
				pfad->ftCreationTime = wfd.ftCreationTime;
				pfad->ftLastAccessTime = wfd.ftLastAccessTime;
				pfad->ftLastWriteTime = wfd.ftLastWriteTime;
				pfad->nFileSizeHigh = wfd.nFileSizeHigh;
				pfad->nFileSizeLow = wfd.nFileSizeLow;

				fRc = 1;

			}
			else
			{
				/*
				 *  FindFirstFile already called SetLastError() for us.
				 */
				fRc = 0;
			}
		}
		else
		{
			/*
			 *  GetFileAttributes already called SetLastError() for us.
			 */
			fRc = 0;
		}
	}
	else
	{
		/*
		 *  Unknown info level.
		 */
		SetLastError(ERROR_INVALID_PARAMETER);
		fRc = 0;
	}

	return fRc;
}

/*
 * The stub that probes to decide which version to use.
 */
static int32 WINAPI Probe_GetFileAttributesEx(LPCTSTR ptszFile, GET_FILEEX_INFO_LEVELS level, LPVOID pv)
{
	HINSTANCE hinst;
	FARPROC fp;
	int32 fRc;
	int32(CALLBACK * RealGetFileAttributesEx) (LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID);

	hinst = GetModuleHandle(TEXT("KERNEL32"));
#ifdef UNICODE
	fp = GetProcAddress(hinst, "GetFileAttributesExW");
#else
	fp = GetProcAddress(hinst, "GetFileAttributesExA");
#endif

	if (fp)
	{
		*(FARPROC *) & RealGetFileAttributesEx = fp;
		fRc = RealGetFileAttributesEx(ptszFile, level, pv);

		if (fRc || GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
			GetFileAttributesEx = RealGetFileAttributesEx;
		else
		{
			GetFileAttributesEx = Emulate_GetFileAttributesEx;
			fRc = GetFileAttributesEx(ptszFile, level, pv);
		}
	}
	else
	{
		GetFileAttributesEx = Emulate_GetFileAttributesEx;
		fRc = GetFileAttributesEx(ptszFile, level, pv);
	}

	return fRc;

}

int32(CALLBACK * GetFileAttributesEx) (LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID) = Probe_GetFileAttributesEx;

#endif /* COMPILE_NEWAPIS_STUBS */
#endif /* WANT_GETFILEATTRIBUTESEX_WRAPPER */

/*****************************************************************************
 *
 * IsDebuggerPresent
 *
 *****************************************************************************/

#ifdef WANT_ISDEBUGGERPRESENT_WRAPPER

#define IsDebuggerPresent _IsDebuggerPresent

extern int32(CALLBACK * IsDebuggerPresent) (VOID);

/*
 * Exactly one file should define this symbol.
 */
#ifdef COMPILE_NEWAPIS_STUBS

/*
 * The version to use if we are forced to emulate.
 */
static int32 WINAPI Emulate_IsDebuggerPresent(VOID)
{
	/* No way to tell, so just say "no". */
	return 0;
}

/*
 * The stub that probes to decide which version to use.
 */
static int32 WINAPI Probe_IsDebuggerPresent(VOID)
{
	HINSTANCE hinst;
	FARPROC fp;
	int32(CALLBACK * RealIsDebuggerPresent) (VOID);

	hinst = GetModuleHandle(TEXT("KERNEL32"));
	fp = GetProcAddress(hinst, "IsDebuggerPresent");

	if (fp)
		*(FARPROC *) & IsDebuggerPresent = fp;
	else
		IsDebuggerPresent = Emulate_IsDebuggerPresent;

	return IsDebuggerPresent();
}

int32(CALLBACK * IsDebuggerPresent) (VOID) = Probe_IsDebuggerPresent;

#endif /* COMPILE_NEWAPIS_STUBS */
#endif /* WANT_ISDEBUGGERPRESENT_WRAPPER */

#ifdef __cplusplus
}
#endif /* __cplusplus */
